1B11 Programming Exercises 5

Marking finishes Friday 26th January 2001

It is recommended that you finish by Friday 19th January


Purpose: Testing.

Goal: Complete as many of these questions as you can. If you are keeping up, you need to do at least the Core questions. The Additional questions are more challenging and are designed to stretch the more confident programmers. Don't worry if you can't do them now, but be prepared to come back and try them later on in the term.

Marking: All questions are binary marked.
To get a question marked, print out the program you have written and a cover sheet from the 1b11 web page. Fill in the cover sheet and attach it to the program listing, then take the work to your lab group demonstrator for marking. The demonstrator may want to see a demonstration of your program running. An answer can be submitted as many times as you like, until it is satisfactory.
All printing should be done on the line printer (a4lp) - don't use up your laser printer quota!

NOTE: You must keep all marked work as it forms a record of your progress. At the end of the course you are required to resubmit all work.


Example of testing a class

This is the code from the example presented in the lectures. Consider a class such as the following:

public class Person
{
private String name ;
private String phoneNumber ;

public Person(String aName, String aNumber)
{
name = aName ;
phoneNumber = aNumber ;
}

public String getName()
{
return name ;
}

public String getNumber()
{
return phoneNumber ;
}
}

A test harness class can be written to create and uses objects of the class to check that everything works properly. An example test harness class is:

public class TestPersonClass

{

  private Person p1 ;

  private Person p2 ;

  private int count = 0 ;

  private int fail = 0 ;

  

  public void setUp()

  {

    count++ ;

    p1 = new Person("P1","123") ;

    p2 = new Person("P2","456") ;    

  }

  

  public void finish()

  {

    System.out.println("\nTests run: " + count) ;

    System.out.println("Tests OK: " + (count - fail)) ;

    System.out.println("Tests failed: " + fail) ;

  }

  

  public void assertEquals(String s1, String s2, String method)

  {

    if (!s1.equals(s2))

    {

      fail++ ;

      System.out.print("\nTest failed in method: " + method + " - ") ;

      System.out.println(s1 + " not equal to " + s2) ;

    }

    else

    {

      System.out.print(".") ;

    }

  }

  

  public void testName()

  {

    assertEquals(p1.getName(),"P1","testName") ;

    assertEquals(p2.getName(),"P2","testName") ;

  }



  public void testNumber()

  {

    assertEquals(p1.getNumber(),"123","testNumber") ;

    assertEquals(p2.getNumber(),"456","testNumber") ;

    // deliberate fail, for example of what happens when a test fails

    assertEquals(p2.getNumber(),"010","testNumber") ;

  }

  

  public static void main(String[] args)

  {

    TestPersonClass t = new TestPersonClass() ;

    t.setUp() ;

    t.testName() ;

    t.setUp() ; // Must re-initialise before each set of tests.

    t.testNumber() ;

    t.finish() ;

  }

}

Notice that the test harness class does all the work of comparing values to see if they are correct and also keeps track of how the tests are going. When writing your own test classes use this example as a template, adding new assert methods to compare different kinds of values. Where a lot of test data is involved it may be necessary to store the data in files.

When you have a program with a number of classes you need to write a test harness class corresponding to each class. Rather than run each test class separately you want to write a TestAll class that creates each test object in turn and runs it. In that case you will want to modify the test harness above so that the contents of the static main method goes into a normal instance method:


  public void runTests()

  {

    setUp() ;

	testName() ;

	setUp() ; // Must re-initialise before each set of tests.

	testNumber() ;

	finish() ;

  } 





  public static void main(String[] args)

  {

    (new TestPersonClass()).runTests() ;

  }

The TestAll class will then contain a main method of this form:

  public static void main(String[] args)

  {

    (new TestPersonClass()).runTests() ;

    (new AnotherClass()).runTests() ;

    (new YetAnotherClass()).runTests() ;

  }

Extend these ideas as you see fit.


Core questions

Q5.1 Test the square root method provided by the Java class Math (i.e., the Math.sqrt method, see the Javadoc for more information).

Do the following:

How accurate is Math.sqrt? To how many decimal places can it be relied on? Does Java round values correctly?

Also, how do you know that the square roots you calculate to compare with those produced by Math.sqrt are themselves correct?


Q5.2 Locate or design a square root algorithm and write your own sqrt method. Then repeat Q5.1 using your method. How accurate is your version of sqrt compared to the Math.sqrt version?

Hint: A square root value is an approximation to some number of decimal places. If y is the square root of x, then y^2 = x, so x / y = y. Guess a value for y, see if x / y is close enough to y, if not try a better guess. Or investigate the Newton-Raphson method for finding the square root of a number.


Q5.3 Write your own Date class to represent dates (don't make use of any of the Java library Date or Calendar classes!). As well as methods to access and set the date, provide methods to add and subtract days, months and years from dates, and to find the number of days between two dates. Test your date class by writing a test harness class, along the lines of the one shown in the introduction.

Your class must correctly deal with dates ranging from 1900 to 3000.


Q5.4 Remember your mini-project from the end of last term? Write a collection of test harness classes to test all the classes you wrote. Also write a TestAll class to run all the tests. If you don't have a mini-project or want to work with some different code then use one of the example answers from the 1b11 web page. Alternatively, test someone-else's mini-project and see how well it really works!

Note that to do thorough testing you may need access to instance variables in an object of another class. This can be achieved by removing the private keyword from the instance variable declaration.


Additional Questions

Q5.5 Write a complex number class and a test harness class to test it (write the test harness first!). Include a suitable range of arithmetic operations in your complex class.


Q5.6 Write test harness classes to thoroughly test the KeyboardInput, FileInput and FileOutput classes used in previous exercises.